#include "tr_int.h"
#include "tr_debug.h"

/* Author: Ratish J. Punnoose, 2006
 * This file is part of TiROS, the Tickless Real-Time Operating System.
 * Copyright(c) 2006, 2007: Ratish J. Punnoose.
 * Copyright(c) 2006 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
 * certain rights in this software. 
 * 
 * TiROS is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 or (at your option) any later version.
 *
 * TiROS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with TiROS; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * As a special exception, if other files instantiate templates or use macros
 * or inline functions from this file, or you compile this file and link it
 * with other works to produce a work based on this file, this file does not
 * by itself cause the resulting work to be covered by the GNU General Public
 * License. However the source code for this file must still be made available
 * in accordance with section (3) of the GNU General Public License.
 *
 * This exception does not invalidate any other reasons why a work based on
 * this file might be covered by the GNU General Public License.
 *
 */




#ifdef TIROSINT_DATA_DEBUG

/* Map result of sizeof
 * result   padded_size   mapped value
 * 1            1          0
 * 2            2          1
 * 3            4          2
 * 4            4          2
 * 5            8          3
 * 6            8          3
 * 7            8          3
 * 8            8          3
 */
/* Go from 1 to 8 */
static const unsigned char log2_map[8] = {0, 1, 2, 2, 3, 3, 3, 3 };

/* Example: TIROS_DEBUG_SETSZ(tid_t) */
#define TIROS_DEBUG_SETSZ(datatype) \
        TIROS_DEBUG_SETNUMBYTES( log2_map[sizeof(datatype)-1], datatype ## _OFFSET)


/* Write out integer/pointer bytes to buffer (of type unsigned char)
 * Define a temporary integer variable tmp in your function before
 * using this */
#define WRITE_BYTES(buffer, data_val)  			   \
             for(tmp = sizeof(data_val);  tmp; tmp--) {  \
		 *buffer++ = (unsigned char) data_val;     \
                  data_val = data_val >> 8; }  		   


int osint_snapshot(unsigned char *buf_orig, unsigned int memsz)
{
	tid_t 		myid;
	int 		i;
	trtime_t 	curr_time;
	trintdata_t 	gdata; /* Global data */
	trlistdata_t 	ldata; /* List data */
	struct TCB *tcblist;
	int tmp;

	uint8_t utmp8;
	uint32_t utmp32;
	tid_t tmptid;
	osword_t tmpword;
	osptrword_t tmpptr;
	flag_t tmpflagt;
	subtime_t tmpsubtime;

	unsigned char *buffer = buf_orig;
	OS_CRITICAL_ENABLE();

		
	if ( buf_orig == 0)  
		return TIROS_DEBUG_DATA_SZ;

	if (memsz < TIROS_DEBUG_DATA_SZ )
		return ERR_FAILED;


	myid = os_self_tid();
	os_time_get(&curr_time);	
	osint_gettrdata(&gdata);
	tcblist = gdata.tcblist;
	osint_getlistdata(&ldata);
	

	/* Write Header */

	/* Options */
	utmp8 = TIROS_DEBUG_PRIOSORT_SET( TIROS_PRIO_SORT_METHOD) |
#ifdef TIROS_REGISTER_PASSING
		TIROS_DEBUG_REG_PASSING |
#endif
#ifdef TIROS_STK_CHECK
		TIROS_DEBUG_STKCHK |
#endif
		0;
	WRITE_BYTES(buffer, utmp8);


	/* data_sizes 1 */
	utmp8 =  TIROS_DEBUG_SETSZ(tid_t ) |
		TIROS_DEBUG_SETSZ(subtime_t) |
		TIROS_DEBUG_SETSZ(flag_t) |
		TIROS_DEBUG_SETSZ( osword_t );
	WRITE_BYTES(buffer, utmp8);


	/* data sizes 2 */
	utmp8 = TIROS_DEBUG_SETSZ(osptrword_t) ;
	WRITE_BYTES(buffer, utmp8);


	/* Maximum tasks */
	utmp8 = TIROS_MAX_PROCS;
	WRITE_BYTES(buffer, utmp8);


	/* Write Global Data */

	/* Write time */
	utmp32 = curr_time.units;	 WRITE_BYTES(buffer, utmp32 );
	tmpsubtime = curr_time.subunits; WRITE_BYTES(buffer, tmpsubtime );

	OS_CRITICAL_BEGIN();

	tmptid = gdata.running_task;     WRITE_BYTES(buffer, tmptid);
	tmptid = ldata.readylisthead;    WRITE_BYTES(buffer, tmptid);
	tmptid = ldata.waitlisthead;     WRITE_BYTES(buffer, tmptid);
	tmptid = gdata.isr_nesting;      WRITE_BYTES(buffer, tmptid);

	tmpword = gdata.ctxt_switch_cnt; WRITE_BYTES(buffer, tmpword);

	/* If this is being called from an ISR, use the running task
	 * as myid */
	if (myid == ILLEGAL_ELEM)
		myid = gdata.running_task;


	for(i=0; i< TIROS_MAX_PROCS; i++) {

		/* Copy the tcb structure */
		tmpptr = (osptrword_t) tcblist->ctxt_ptr;
		WRITE_BYTES(buffer, tmpptr);
		
		tmptid = tcblist->priority;     WRITE_BYTES(buffer, tmptid);
		tmptid = tcblist->eff_priority; WRITE_BYTES(buffer, tmptid);
		utmp8 = tcblist->flags;         WRITE_BYTES(buffer, utmp8);
		utmp8 = tcblist->num_mutexes;   WRITE_BYTES(buffer, utmp8);

		utmp32 = tcblist->timeout.units; WRITE_BYTES(buffer,utmp32);
		tmpsubtime = tcblist->timeout.subunits; WRITE_BYTES(buffer,tmpsubtime);
		
		tmpptr = (osptrword_t)tcblist->lock_ptr;
		WRITE_BYTES(buffer, tmpptr);

		tmpflagt = (osptrword_t)tcblist->event_bits;
		WRITE_BYTES(buffer, tmpflagt);

#ifndef TIROS_REGISTER_PASSING
		tmpptr = (osptrword_t)tcblist->paramret_val;
		WRITE_BYTES(buffer, tmpptr);
#endif


#ifdef TIROS_STK_CHECK
		tmpptr = (osptrword_t)tcblist->base_stk_ptr;
		WRITE_BYTES(buffer, tmpptr);
		tmpword = tcblist->stksize;
		WRITE_BYTES(buffer, tmpword);
#endif

		/* Done writing TCB structure */




		if (  tcblist->ctxt_ptr == TR_ILLEGAL_ADDR ) {
			/* data_per_task */
			/* task_pc */
			tmpptr = (osptrword_t)TR_ILLEGAL_ADDR; 
			WRITE_BYTES(buffer, tmpptr);
#ifdef TIROS_STK_CHECK
			/* curr_stkusage */
			tmpword = 0;	WRITE_BYTES(buffer, tmpword);
#endif
		} else {
			/* data_per_task */
			/* task_pc */
			tmpptr = (osptrword_t)
				hal_pc_from_ctxt(tcblist->ctxt_ptr);   
			WRITE_BYTES(buffer, tmpptr);
#ifdef TIROS_STK_CHECK
			/* curr_stkusage */
			tmpword = hal_stkusage_curr(tcblist->ctxt_ptr,
						    tcblist->base_stk_ptr,
						    tcblist->stksize);
			WRITE_BYTES(buffer, tmpword);
#endif
		}

#ifdef TIROS_STK_CHECK
		if (tcblist->ctxt_ptr == TR_ILLEGAL_ADDR) {
			tmpword = 0; WRITE_BYTES(buffer, tmpword);
		} else {
			/* max_stkusage */
			tmpword =  hal_stkusage_max(tcblist->ctxt_ptr,
						    tcblist->base_stk_ptr,
						    tcblist->stksize);
			WRITE_BYTES(buffer, tmpword);
		}
#endif

#if (TIROS_PRIO_SORT_METHOD == 1)
		tmptid = *ldata.rdywt_listmeta++;
		WRITE_BYTES(buffer, tmptid);
		tmptid = *ldata.lock_listmeta++;
		WRITE_BYTES(buffer, tmptid);
#endif
		tcblist++;


	}

	OS_CRITICAL_END();

	return (buffer - buf_orig);


}



#endif
